package conf

import (
	"context"
	"database/sql"
	"fmt"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

var config *Config
var db *sql.DB

type Config struct {
	App   *App   `toml:"app"`
	Log   *Log   `toml:"log"`
	MySQL *MySQL `toml:"mysql"`
}

type App struct {
	Name string `toml:"name" env:"APP_NAME"`
	Host string `toml:"host" env:"APP_HOST"`
	Port string `toml:"port" env:"APP_PORT"`
	Key  string `toml:"key" env:"APP_KEY"`
}

func newDefaultApp() *App {
	return &App{
		Name: "GoRestFulApi",
		Host: "127.0.0.1",
		Port: "8080",
		Key:  "user_key",
	}
}

type Log struct {
	Path string `toml:"path" env:"LOG_PATH"`
}

func newDefaultLog() *Log {
	return &Log{}
}

type MySQL struct {
	Host     string `toml:"host" env:"MYSQL_HOST"`
	Port     string `toml:"port" env:"MYSQL_PORT"`
	UserName string `toml:"username" env:"MYSQL_USERNAME"`
	Password string `toml:"password" env:"MYSQL_PASSWORD"`
	Database string `toml:"database" env:"MYSQL_DATABASE"`
	lock     sync.Mutex
}

func newDefaultMySQL() *MySQL {
	return &MySQL{
		Host:     "127.0.0.1",
		Port:     "3306",
		UserName: "root",
		Password: "o4763948k",
		Database: "go_course",
	}
}

func (m *MySQL) getDBConn() (*sql.DB, error) {
	var err error
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&multiStatements=true", m.UserName, m.Password, m.Host, m.Port, m.Database)
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("mysql connect to mysql<%s> error: %s", dsn, err.Error())
	}

	timeout, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancelFunc()
	if err := db.PingContext(timeout); err != nil {
		return nil, fmt.Errorf("ping mysql<%s> error: %s", dsn, err.Error())
	}
	return db, nil
}

func (m *MySQL) GetDb() (*sql.DB, error) {
	m.lock.Lock()
	defer m.lock.Unlock()
	if db == nil {
		db, err := m.getDBConn()
		return db, err
	}
	// 加载全局数据
	return db, nil
}

func newConfig() *Config {
	config = &Config{
		App:   newDefaultApp(),
		Log:   newDefaultLog(),
		MySQL: newDefaultMySQL(),
	}
	return config
}

func C() *Config {
	return config
}
